<template>
  <n-space vertical>
    <slot name="toolBar">
      <n-space>
        <action-button :actions="props.topActions" :perms="auth ? (modelState?.perms as string) : false" @action:add="handleAdd" />
        <slot name="toolBarEnd" />
        <div v-if="checkedRowKeys && checkedRowKeys.length > 0">
          <n-popconfirm
            :negative-text="null"
            :positive-button-props="{
              type: 'warning'
            }"
            @positive-click="
              () => {
                handleDelete(...(checkedRowKeys || []))
              }
            "
          >
            <template #trigger>
              <n-button v-auth="auth && [`${modelState?.perms}-delete`]" secondary type="error">
                <template #icon>
                  <Icon icon-name="TrashOutline" />
                </template>
                删除
              </n-button>
            </template>
            确认删除{{ checkedRowKeys.length }}条数据？
          </n-popconfirm>
        </div>
      </n-space>
    </slot>
    <n-config-provider :theme-overrides="enableEdit ? editTableThemeOverrides : {}">
      <n-form ref="tableEditFormRef" :model="tableData" :show-label="false" inline :show-feedback="false">
        <n-data-table
          ref="tableRef"
          v-bind="tableProps"
          :columns="columns"
          :loading="loading"
          :data="tableData"
          :pagination="showPage === false ? false : pagination"
          :row-key="(row: T) => row[dateKey ?? 'id'] ?? row?.toString()"
          :remote="true"
          :tree="!!treeField"
          @update:checked-row-keys="checkedRowKeysChangeHandle"
          @update:page="(page: number) => pageChangeHandle({ page })"
          @update:page-size="(pageSize: number) => pageChangeHandle({ pageSize })"
          @update:sorter="sortChangeHandle"
        >
          <template v-if="$slots.loading" #loading>
            <slot name="loading" />
          </template>
          <template v-if="$slots.empty" #empty>
            <slot name="empty" />
          </template>
        </n-data-table>
      </n-form>
    </n-config-provider>
  </n-space>
</template>

<script lang="ts" setup generic="T extends BaseModel">
  import { DataTableInst, DataTableSortState, FormItemGiProps, GlobalThemeOverrides, NForm, NFormItem, PaginationProps, useThemeVars } from 'naive-ui'
  import type { TableColumn } from 'naive-ui/es/data-table/src/interface'
  import { ActionOption, commonActions } from '@/components/ActionButton/commonActions'
  import ActionButton from '@/components/ActionButton/index.vue'
  import { cloneDeep, isArray } from 'lodash-es'
  import { BaseModel } from '@/hooks/useModel'
  import { appSetting } from '@/config/app.config'
  import { createInputComponent } from '@/components/ext/index'
  import type { ETableInst, ETableProps, ETableSlots, LoadData } from './types'
  import { EzModelOptions, FieldOption, FormDataTypeEnum, FormType, FormTypeEnum, IFormData, TreeField } from 'easy-descriptor'
  import { EzFieldOption } from '@/hooks/useModel/types'
  import { useCompRef } from '@/hooks/useCompRef'
  import dayjs from 'dayjs'
  import { useModelApi } from '@/hooks/useApi'
  import EDictTag from '@/components/ext/input/EDictTag.vue'
  import type { Ref } from 'vue'

  defineOptions({
    name: 'ETable'
  })
  // useModel() called with prop "checked-row-keys" which is not declared.

  const props = withDefaults(defineProps<ETableProps<T>>(), {
    actions: 'default',
    topActions: () => ['add'],
    tableProps: () => ({}),
    beforeQuery: async (queryData: IFormData<T>) => queryData,
    data: () => [],
    showPage: undefined,
    enableEdit: false,
    autoLoad: true,
    dataKey: 'id',
    auth: true
  })

  const tableEditFormRef = useCompRef(NForm)
  const checkedRowKeys = defineModel<string[]>('checkedRowKeys', { default: () => [] })

  const modelState = ref<EzModelOptions<T>>()
  modelState.value = props.modelOptions
  const actions = props.actions
  const { batchRemove } = useModelApi<T>(modelState.value.api)

  const handleAdd = () => {
    showForm({}, FormTypeEnum.ADD)
  }
  const handleDelete = async (...ids: DataKey[]) => {
    console.log(`删除第行，id = ${ids}`)

    const resp = await batchRemove({ ids: ids.join(',') })
    if (resp.success) {
      // checkedRowKeys.value.length = 0
      checkedRowKeys.value.splice(0, checkedRowKeys.value.length)
      await load()
    }
  }
  const handleEdit = ({ row, index }) => {
    console.log(`编辑第${index} 行，id = ${row.id}`)
    showForm(row, FormTypeEnum.EDIT)
  }

  // const a = [
  //   {
  //     action: 'edit',
  //     html: '编辑',
  //     text: true,
  //     icon: 'antd:EditTwotone',
  //     type: 'primary',
  //     isDisabled: true,
  //     autoShow: true,
  //     perms: 'update'
  //     // handle: (row, index) => {
  //     //   console.log(`action edit row${index}:`, row)
  //     // }
  //   },
  //   {
  //     action: 'delete',
  //     html: '删除',
  //     text: true,
  //     actionConfirm: true,
  //     icon: 'TrashOutline',
  //     type: 'error',
  //     autoShow: true,
  //     perms: 'delete'
  //     // handle: (row, index) => {
  //     //   console.log(`action delete row${index}:`, row)
  //     // }
  //   }
  // ]
  const defaultActions: ActionOption[] = [
    {
      ...commonActions.find(({ action }) => action === 'edit')!,
      // action: 'edit',
      // html: '编辑',
      // text: true,
      // type: 'primary',
      // isDisabled: true,
      // autoShow: true,
      // perms: 'update',
      handle: handleEdit
    },
    {
      ...commonActions.find(({ action }) => action === 'delete')!,
      // action: 'delete',
      // html: '删除',
      // text: true,
      // type: 'error',
      // autoShow: true,
      // perms: 'delete',
      handle: ({ row: { id } }) => handleDelete(id)
    }
  ]

  const rowActionRender = (row: T, index: number) => {
    let tempActs: any = actions
    if (typeof actions === 'function') {
      tempActs = actions(row, index)
    }

    let _actions: ActionOption[] = []
    if (typeof tempActs === 'object') {
      tempActs.forEach((act) => {
        if (typeof act === 'string') {
          const option = defaultActions.find(({ action }) => act == action)
          if (option) {
            _actions.push(option)
          }
        } else {
          _actions.push(act)
        }
      })
    } else {
      _actions = defaultActions
    }
    return h(ActionButton, {
      align: 'center',
      perms: modelState.value?.perms,
      actions: _actions,
      data: { row, index },
      divider: true
    })
  }

  if (actions !== false) {
    modelState.value.fields['actions'] = {
      label: '操作',
      key: 'action',
      fixed: 'right',
      align: 'center',
      // width: actions === 'default' ? 140 : typeof actions !== 'boolean' ? actions.length * 70 : 0,
      render: rowActionRender
    } as any
  }

  const editTableThemeOverrides = ref<GlobalThemeOverrides>({
    common: {
      borderRadius: '0',
      inputColor: 'transparent'
    },
    DataTable: {
      tdPaddingSmall: '0',
      tdPaddingMedium: '0',
      tdPaddingLarge: '0',
      borderRadius: '3px'
    },
    Input: {
      border: 'none',
      borderDisabled: 'none'
    },
    InternalSelection: {
      border: 'none',
      borderDisabled: 'none'
    }
  })

  /**
   * 创建表单项属性
   * @param field
   */
  const createFormItemProps = (field: FieldOption<T>): FormItemGiProps => {
    const { key, path, rule, rulePath, required, formSpan, label } = field
    const _rule: any[] = []
    if (rule) {
      if (isArray(rule)) {
        _rule.push(...rule.filter(({ formTypes }) => !formTypes || formTypes.includes(FormTypeEnum.EDIT_TABLE as any)))
      } else if (!rule.formTypes || rule.formTypes.includes(FormTypeEnum.EDIT_TABLE as any)) {
        _rule.push(rule)
      }
    }
    const _key = (path || key).toString()
    return { path: _key, rule: _rule, rulePath, required, label, span: formSpan }
  }

  const indexRender: EzFieldOption<T>['render'] = (_, index) => h('div', { class: 'index-column' }, index + 1)

  const commonFields: Record<'$index' | '$selection', EzFieldOption<T>> = {
    $index: {
      key: 'index',
      title: '#',
      hidden: ['query', 'form'],
      width: 50,
      align: 'center',
      render: indexRender
    } as EzFieldOption<T>,
    $selection: {
      type: 'selection',
      key: 'selection',
      width: 50,
      align: 'center',
      hidden: ['query', 'form']
    }
  }

  const columns = computed(() =>
    Object.values(modelState.value?.fields || {})
      .filter(({ hidden }) => !(hidden === true || (hidden && hidden?.includes('list'))))
      .map((col: FieldOption<T>) => {
        if (col.key && commonFields[col.key]) {
          return {
            ...commonFields[col.key],
            ...col
          }
        }

        if (props.enableEdit) {
          return {
            ...col,
            title: col.label,
            render: (_, index: number) => {
              const fData = toRef<IFormData<T>>(tableData.value?.[index] ?? {})
              const child = createInputComponent<T>({
                field: col,
                formData: fData as Ref<IFormData<T>>,
                target: props.instance,
                formType: FormTypeEnum.EDIT_TABLE
              })
              // const child = {} as any
              // console.log('child', child.props)
              const disabled = child.props?.disabled
              return h(
                NFormItem,
                {
                  ...createFormItemProps(col),
                  class: ['editable-cell', disabled ? 'disabled-cell' : '']
                },
                () => child
              )
            }
          } as TableColumn
        } else {
          const commonRender = ({ dataType, render, key, dict }: FieldOption<T>) => {
            switch (dataType) {
              case FormDataTypeEnum.DATETIME:
                return (row: T) => (row[key!] ? dayjs(row[key!]).format('YYYY-MM-DD HH:mm:ss') : '')
              case FormDataTypeEnum.DATE:
                return (row: T) => (row[key!] ? dayjs(row[key!]).format('YYYY-MM-DD') : '')
              case FormDataTypeEnum.TIME:
                return (row: T) => (row[key!] ? dayjs(row[key!]).format('HH:mm:ss') : '')
              default:
                if (dict && !render) {
                  return (row: T) => h(EDictTag, row[`${key!.toString()}_dict`], '')
                }
                return render
              // switch (key) {
              //   case 'enableFlag':
              //     return (row: T) => h(EDictTag, row['enableFlag_dict'], '')
              //   default:
              //     console.dir('render', render)
              //     return render
              // }
            }
          }
          return { ...col, title: col.label, render: commonRender(col) } as TableColumn
        }
      })
  )

  // const slots = useSlots()
  const defaultPage: PaginationProps = {
    page: 1,
    pageSize: appSetting.pageSizes[0] || 10,
    pageCount: 0,
    itemCount: 0,
    showQuickJumper: true,
    showSizePicker: true,
    pageSizes: appSetting.pageSizes,
    prefix: ({ itemCount }) => `共 ${itemCount} 项 `
  }

  const pagination = ref<PaginationProps>(cloneDeep(defaultPage))
  const sort = ref<Record<string, string>>({})

  const load: LoadData<T> = async (params?: any) => {
    loading.value = true
    if (!props.loadData) {
      tableData.value = props.data || []
      return {} as any
    }
    const pageData = await props.loadData(await props.beforeQuery(params)).finally(() => {
      loading.value = false
    })
    const { records, size, current, total, pages } = pageData
    tableData.value = records

    pagination.value = {
      ...pagination.value,
      page: current,
      pageSize: size,
      itemCount: total,
      pageCount: pages
    }
    return pageData
  }

  const pageChangeHandle = (page: PaginationProps) => {
    Object.keys(page).forEach((key) => {
      pagination.value[key] = page[key]
    })
    console.log('pageChangeHandle', pagination.value)
    nextTick(() => {
      load()
    })
  }

  const checkedRowKeysChangeHandle = (val: Array<string | number>) => {
    checkedRowKeys.value = val.map((v) => v.toString())
  }

  const sortChangeHandle = (options: DataTableSortState | DataTableSortState[] | null) => {
    sort.value = {}
    if (options) {
      const sortList = isArray(options) ? options : [options]
      // const sortColumns = {}
      sortList.forEach(({ columnKey, order }) => {
        order && (sort.value[columnKey] = order)
      })
    }
    load({ sort: JSON.stringify(sort.value) })
  }

  const tableRef = defineModel<DataTableInst>('bindRef')

  const loading = ref<boolean>(false)
  const tableData = ref<any[]>([])
  tableData.value = (props.data ?? []) as any[]
  // const { page: loadPage } = useModelApi<T>(modelState.api)

  const treeField = modelState.value.tree as TreeField<T>

  const emit = defineEmits<{
    (evt: 'showForm', formData: IFormData<T>, type: FormType): void
  }>()

  const showForm = (formData: any, type: FormType) => {
    emit('showForm', formData, type)
    if (props.formInst?.value) {
      props.formInst.value.open(type, formData)
    } else {
      !props.enableEdit && console.warn(`${props.modelOptions.name}: formInst is not defined`)
    }
  }

  const pageParams = computed<PaginationProps & { sort?: string }>(
    () =>
      ({
        ...pagination.value,
        sort: JSON.stringify(sort.value)
      }) as PaginationProps & { sort?: string }
  )

  defineExpose<ETableInst<T>>({
    reload: load,
    getPageParams: () => pageParams.value,
    getCheckedRows: () => tableData.value?.filter(({ id }) => id && checkedRowKeys.value?.includes(id)) || []
  })

  const slots = defineSlots<ETableSlots<T>>()

  onMounted(async () => {
    const slotsTmp = toRaw(slots)
    Object.keys(slotsTmp).forEach((slotName) => {
      if (slotName.indexOf('#') === 0) {
        const colKey = slotName.replace('#', '')
        const col = columns.value?.find((column) => column['key'] === colKey)
        if (col) {
          ;(col as TableColumn)['render'] = (row: any, index: number) => slotsTmp[slotName]?.(row, index)
        }
      }
    })
    tableData.value = props.data || []
    props.autoLoad && (await load())
  })
  const inputColorDisabled = useThemeVars().value.inputColorDisabled //computed(() => themeVars.value.inputColorDisabled)
</script>

<style lang="less">
  .editable-cell {
    //&:child {
    //  background: transparent !important;
    //}

    .n-base-suffix {
      display: none;
    }

    //.n-form-item-feedback-wrapper {
    //  margin-bottom: -24px;
    //}
  }

  td.n-data-table-td:has(div.disabled-cell) {
    background-color: v-bind(inputColorDisabled);

    & div.n-input--disabled,
    div.n-base-selection--disabled,
    div.n-base-selection-label {
      background-color: transparent;
    }
  }
</style>
